One, foreword

In the last post, we completed our first custom component, The Circular Countdown Progress Bar, which supports both CocoaPods and SPM, but does not form a separate library. As mentioned in this article, we will introduce components to the AD page to improve the page; Of course, if this article just improves the page, it might be too much, after all, it’s relatively easy to use. Therefore, I also incidentally put the eighth, although given the source code, but no analysis of the content (timer) analysis, as well as through the code to write constraints need to pay attention to the point, and the reason is also analyzed to you.

Two, improve the advertising page

To be lazy, I used SPM’s native integration approach, just dragging the entire directory into the project and adding the framework.

Open AdvertiseViewController, import & lazy Init

import CircleProgressView
class AdvertiseViewController: BaseViewController {
    .
    // Lazily initializes the View and does not write the location and size as given by the following constraints
    lazy var progress: CircleProgressView = CircleProgressView(frame: CGRect.zero)
    .
}
Copy the code

2.2 place the circular countdown progress bar control

class AdvertiseViewController: BaseViewController {
        func countDown(a) {
            progress.translatesAutoresizingMaskIntoConstraints = false
    
            // Allow the user to click and skip to the next page
            progress.isUserInteractionEnabled = true
            progress.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.terminer)))
            view.addSubview(progress)
            
            / / constraints:
            // 1.
            // 2. Top margin = 50
            // 3. Trailing margin = -30
            NSLayoutConstraint.activate([
                progress.widthAnchor.constraint(equalToConstant: 64.0),
                progress.heightAnchor.constraint(equalToConstant: 64.0),
                progress.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
                progress.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30)]).}}Copy the code

2.3. Modify viewDidLoad to start countdown & Start animation

class AdvertiseViewController: BaseViewController {
    override func viewDidLoad(a) {
        super.viewDidLoad()
        view.backgroundColor = .kRed
        countDown() // timeCountDown() = countDown()
    }
    
    func countDown(a) {
        .
        // Start animation, countdown time, animation from 0.0 -> 1.0
        progress.setProgress(1.0, duration: Double(seconds), animated: true)
        
        // Start the countdown
        timeCountDown()
    }
}
Copy the code

2.4 in the countdown process, our reading seconds should also keep changing

class AdvertiseViewController: BaseViewController {
    func timeCountDown(a) {
        timer.schedule(deadline: .now(), repeating: .seconds(1))
        timer.setEventHandler(handler: {
            DispatchQueue.main.async { [weak self] in
                
                // If the timer is less than or equal to 0, end the timer and switch between two Windows
                if self!.seconds < = 0 {
                    self!.terminer()
                } else {
                    // In the countdown, keep setting the contents of the label
                    self!.progress.setContent(String(self!.seconds))
                }
                self!.seconds - = 1
            }
        })
        timer.resume()
    }
}
Copy the code

2.5. Finally, modify our termination method (add @objc)

class AdvertiseViewController: BaseViewController {
    @objc func terminer(a) {
        .}}Copy the code

Three, constraints layout matters needing attention

I don’t know if you noticed the following code:

class AdvertiseViewController: BaseViewController {
    func countDown(a) {
        progress.translatesAutoresizingMaskIntoConstraints = false
        .
        view.addSubview(progress)

        NSLayoutConstraint.activate([
            .
        ])
        .}}Copy the code

Before the code sets constraints, we all need to view the translatesAutoresizingMaskIntoConstraints attribute is set to false? Of course, if you’ve been using xiBs for layout and setting constraints, you probably won’t notice, but you probably know. Also, even with XIBS, there is a code layout problem, so if you tell me that everyone does it, so you do it, well, this article will take a look at it and help you better understand why.

In general, we create a view in two ways:

  • Through the code to create the view (default is OC translatesAutoresizingMaskIntoConstraints: YES/Swift: true, hereinafter the same).
  • To create the view through the IB (translatesAutoresizingMaskIntoConstraints default is (autoresize layout: YES, autolayout layout: NO));

In the above code, we use constrained layout (Autolayout).

Why use translatesAutoresizingMaskIntoConstraints constraint layout, is set to false (OC: NO)?

Because translatesAutoresizingMaskIntoConstraints meant the frame layout will be automatically converted to constraint layout, the result of the conversion is needed for this view is automatically add all constraints, If we add our own constraints to the view, we are bound to have constraint conflicts.

Four, common timer (only for use, not to expand)

Timers commonly used in iOS are as follows:

  • NSTimer;
  • CADisplayLink;
  • The GCD.
  • Simulation in other ways
advantages disadvantages
NSTimer Using a simple The timing is not accurate because of Runloop
CADisplayLink High precision The CPU load will affect the triggering event, and the triggering event will cause frame drop if the triggering event is longer than the triggering interval.
GCD More accurate Basically unaffected by other influences

Our AD page, the timer is actually GCD, it is a low-level way, rely on the system kernel timer, as for the other, this article will not expand, the mainstream commonly used general preferred GCD, because more accurate, and will not have cyclic reference, memory leaks and other problems.

After Swift4, creating GCD timer is much easier, just one sentence:

// Timer associates a queue (global.global(),.main, or something else)
// After that, timer.seteventhandler can put the event to be processed into this queue
lazy var timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.global())
Copy the code

Later, we can call schedule to set when timer starts, how long interval, whether delay is allowed and how long delay is acceptable:

// Deadline is the start time, for example: start immediately.now() // repeating is the interval time, for.never or.default it will only trigger once // leeway in some cases (e.g. @available(swift 4) public func Schedule (deadline: DispatchTime, repeating interval: DispatchTimeInterval = .never, leeway: DispatchTimeInterval = .nanoseconds(0))Copy the code

Since our timer has already been created but is not yet running, the method to start it is:

timer.resume()
Copy the code

Suspension is:

timer.suspend()
Copy the code

Cancellation is:

timer.cancel()
Copy the code

At this point, [double window + advertising page + the first component: circular countdown progress bar] we are completely finished sharing, next, start to enter our body: home page. (I have been very busy recently, working on the overall solution of big data offline + real-time, as well as the operation and maintenance system; -_ -!!!!!! The first content on the front page may be a few days late, sorry)

All source code so far: Passgate

If you have any questions, please contact us. Thank you!


This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign