Home>

I made a button that can be used for voice memos at the URL below, but when I added Autolayout, the button became huge.

https://swift-ios.keicode.com/ios/how-to-create-record-button.php

Without Autolayout, it shrinks inside the white circle, but with it, it expands for some reason.
The top is a tableView, the bottom is a UIView, and there are two more views and one button in it.
tableView
UIView (baseView)
->outerCircle
->innerCircle
->recordButton
The width and height are fixed for each baseView, and Horizontal in Container and Vertical in Container are set to 0. Below is the configuration diagram.

The following is the full text, but there was nothing wrong with it until I added the auto layout.
It should be reduced by cornerRadious in the animation, but it will be enlarged on the contrary.
Is this a bug, or is there some auto-layout limitation?

If anyone knows, I would appreciate any advice.

import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioRecorderDelegate, UITableViewDelegate, UITableViewDataSource {
    var isRecording = false
    var recordingSession: AVAudioSession!
    var audioRecorder: AVAudioRecorder!
    var audioPlayer: AVAudioPlayer!
    var w: CGFloat = 0
    var h: CGFloat = 0
    let d: CGFloat = 50
    let l: CGFloat = 28
    var numberOfRecords: Int = 0
    @IBOutlet weak var recordButton: UIButton!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var baseView: UIView!
    @IBOutlet weak var outerCircle: UIView!
    @IBOutlet weak var innerCircle: UIView!
    @IBAction func record (_ sender: Any) {
        // Check if we have an active recorder
        if audioRecorder == nil {
            numberOfRecords + = 1
            let fileName = getDirectory (). appendingPathComponent ("\ (numberOfRecords) .m4a")
            let settings = [AVFormatIDKey: Int (kAudioFormatMPEG4AAC),
                            AVSampleRateKey: 12000,
                            AVNumberOfChannelsKey: 1,
                            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
            // Start audio recording
            do {audioRecorder = try AVAudioRecorder (url: fileName, settings: settings)
                audioRecorder.delegate = self
                audioRecorder.record ()
            } catch {
                displayAlert (title: "Ups !!!", message: "Recording failed")
            }
            UIView.animate (withDuration: 0.2) {
                self.recordButton.frame = CGRect (x: (self.w-self.d)/2, y: (self.h-self.d)/2, width: 30, height: 30)
                self.recordButton.layer.cornerRadius = 3.0
                self.recordButton.layer.masksToBounds = true
            }
        } else {
            // Stopping audio recording
            audioRecorder.stop ()
            audioRecorder = nil
            UserDefaults.standard.set (numberOfRecords, forKey: "myNumber")
            tableView.reloadData ()
            UIView.animate (withDuration: 0.2) {
                self.recordButton.frame = CGRect (x: (self.w-self.self.l)/2, y: (self.h-self.l)/2, width: self.l, height: self.l)
                self.recordButton.layer.cornerRadius = 25
            }
        }
        isRecording =! isRecording
    }
    override func viewDidLoad () {
        super.viewDidLoad ()
        // Setting Up Session
        recordingSession = AVAudioSession.sharedInstance ()
        if let number: Int = UserDefaults.standard.object (forKey: "myNumber") as? Int {
            numberOfRecords = number
        }
        AVAudioSession.sharedInstance (). requestRecordPermission {(hasPermission) in
            if hasPermission {
                print ("ACCEPTED")
            }
        }

    }
    override func viewDidAppear (_ animated: Bool) {
        w = baseView.frame.size.width
        h = baseView.frame.size.height
        initRoundCorners ()
        recordButton.layer.masksToBounds = true
        recordButton.layer.cornerRadius = 25
      }
    // Function that gets path to directoryfunc getDirectory ()->URL {
        let paths = FileManager.default.urls (for: .documentDirectory, in: .userDomainMask)
        let documentDirectory = paths [0]
        return document Directory
    }
    // Function that displays an alert
    func displayAlert (title: String, message: String) {
        let alert = UIAlertController (title: title, message: message, preferredStyle: .alert)
        alert.addAction (UIAlertAction (title: "dismiss", style: .default, handler: nil))
        present (alert, animated: true, completion: nil)
    }
    func recordButtonCustomize () {
        recordButton.layer.masksToBounds = true
        recordButton.layer.cornerRadius = 25.0
    }
    func initRoundCorners () {
        recordButton.layer.masksToBounds = true
        baseView.layer.masksToBounds = true
        baseView.layer.cornerRadius = 10
        baseView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
        outerCircle.layer.masksToBounds = true
        outerCircle.layer.cornerRadius = 31
        outerCircle.backgroundColor = #colorLiteral (red: 1, green: 1, blue: 1, alpha: 1)
        innerCircle.layer.masksToBounds = true
        innerCircle.layer.cornerRadius = 29
        innerCircle.backgroundColor = #colorLiteral (red: 0.1298420429, green: 0.1298461258, blue: 0.1298439503, alpha: 1)
    }
    func tableView (_ tableView: UITableView, numberOfRowsInSection section: Int)->Int {
        return numberOfRecords
    }
    func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath)->UITableViewCell {
        let cell = tableView.dequeueReusableCell (withIdentifier: "cell", for: indexPath)
        cell.textLabel? .text = String (indexPath.row + 1)
        return cell
    }
    func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let path = getDirectory (). appendingPathComponent ("\ (indexPath.row + 1) .m4a")
        do {
            audioPlayer = try AVAudioPlayer (contentsOf: path)
            audioPlayer.play ()
        } catch {
        }
    }
}
  • Answer # 1

    If you add AutoLayout, the constraint you added there will take precedence.frameI don't think it will work if I try to change the value.

    I need to read the article carefully, but some of the articlesThere is no mention of "constraining buttons"..

    For buttons, to summarize the article

    Make the width and height about 50

    baseViewPut under the control of
    Is only mentioned.

    You might think that "2-1. How to make a round button" says "Place the button in a suitable place and make it 50 in width and 50 in height." This width. , The height must be determined appropriately by dragging the Button, or set from the View of the Size Inspector."Not a constraint"Please note that.

    However, in reality, the size of the button is decided in the code, so what you need isbaseViewIt doesn't matter if the size is really suitable, just put it under the control of.

    Also, what you really need to refer to is the completed code at the end of the article, kururinnya
    The code that Mr. is referring to is the code that is being created.

    If it is the completed code that is also published in the article

    showStartButton ()sobaseViewButton placement and rounded corner settings within

    showStopButton ()sobaseViewButton placement and rounded corner settings within
    I am doing.

    Both have "button placement", but when making a rectangle, it is necessary to shift the coordinates by the amount that the outer dimensions have changed, so that calculation has also been added.

    In short

    Manipulate constraints if you want to change dimensions with AutoLayout

    If you operate the frame directly, do not use AutoLayout only for that part
    I think that is the basis, so I think it's a good idea to hold that down.

    By the way, the code that can be verified with the minimum necessary is as follows.

    import UIKit
    class ViewController: UIViewController {
        @IBOutlet weak var recordButton: UIButton!
        @IBOutlet weak var baseView: UIView!
        var isRecording = false// If possible, a more descriptive property name is better
        var w: CGFloat = 0
        var h: CGFloat = 0
        let d: CGFloat = 50
        let l: CGFloat = 28
        override func viewDidLoad () {
            super.viewDidLoad ()
            // Do any additional setup after loading the view.
        }
        override func viewDidAppear (_ animated: Bool) {
            w = baseView.frame.size.width
            h = baseView.frame.size.height
            initRoundCorners ()
            // MARK: This is missing
            showStartButton ()
            // MARK: You don't need the following
            // Set with initRoundCorners ()
            // recordButton.layer.masksToBounds = true
            // Set with showStartButton ()
            // recordButton.layer.cornerRadius = 25
        }
        // MARK: Missing
        func showStartButton () {
            // If you do not set the frame value, the initial position of the button will not be determined accurately.
            recordButton.frame = CGRect (x: (w-d)/2, y: (h-d)/2, width: d, height: d)
            recordButton.layer.cornerRadius = d/2
        }
        // MARK: Missing
        func showStopButton () {
            recordButton.frame = CGRect (x: (w-l)/2, y: (h-l)/2, width: l, height: l)
            recordButton.layer.cornerRadius = 3.0}
        func initRoundCorners () {
            recordButton.layer.masksToBounds = true
            baseView.layer.masksToBounds = true
            baseView.layer.cornerRadius = 10
            baseView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
        }
        @IBAction func record (_ sender: Any) {
            // MARK: Judgment by isRecording for verification
            if! isRecording {
                UIView.animate (withDuration: 0.2) {
                    // MARK: Process with showStopButton ()
                    // self.recordButton.frame = CGRect (x: (self.w-self.d)/2, y: (self.h-self.d)/2, width: 30, height: 30)
                    // self.recordButton.layer.cornerRadius = 3.0
                    // self.recordButton.layer.masksToBounds = true
                    self.showStopButton ()
                }
            } else {
                UIView.animate (withDuration: 0.2) {
                    // MARK: Process with showStartButton ()
                    // self.recordButton.frame = CGRect (x: (self.w-self.self.l)/2, y: (self.h-self.l)/2, width: self.l, height: self. l)
                    // self.recordButton.layer.cornerRadius = 25
                    self.showStartButton ()
                }
            }
            // Toggle true<->false
            isRecording.toggle ()
        }
    }