I was trying to create a UIButton by code, and I want to add the UIButton as a subview into a UITextView.
Here is the code:
private func initTermsTextView() {
let filePath = Bundle.main.path(forResource: "terms", ofType: "html", inDirectory: nil, forLocalization: nil) ?? ""
let htmlString = try! String(contentsOfFile: filePath)
let htmlData = NSString(string: htmlString).data(using: String.Encoding.unicode.rawValue)
let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html]
let attributedString = try! NSMutableAttributedString(data: htmlData!, options: options, documentAttributes: nil)
let font = UIFont(name: "BrandonGrotesque-Regular", size: 20) ?? UIFont.systemFont(ofSize: 20)
attributedString.setFontFace(font: font, color: UIColor.grayMetalic())
let btn: UIButton = UIButton(frame: CGRect(x: 8, y: termsTextView.contentSize.height*11-135, width: 48, height: 48))
btn.setTitle("asdasdasdasda", for: .normal)
btn.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
btn.clipsToBounds = true
btn.setImage(UIImage(named: "uncheckedBox"), for: .normal)
let path = UIBezierPath(rect: CGRect(x: 8, y: termsTextView.contentSize.height*11-135, width: btn.frame.width, height: btn.frame.height))
self.termsTextView.textContainer.exclusionPaths = [path]
self.termsTextView.attributedText = attributedString
self.termsTextView.text.append(TermConditionSingleton.getInstance.setTCGetString())
self.termsTextView.addSubview(btn)
}
Since I have no idea how to add UIButton at the bottom of a UITextView, I did hardcore and count out the UITextView length(text length*11-135) and added the button under the UITextView.
After I was running my code with devices which is above the iPhone 6 (include 6s and plus) and the UIButton has displayed in the UITextView. But when I tried with iPhone 6 plus and iPhone 6s plus, the UIButton sub view did not show in the UITextView and I have no idea why.
Is there any way I can fix this type of problem?
It is always risky to hard code the device type…
If I look at the switch, they are all the same, except one constant, which is probably a number of lines.
Take care when you localize to a different language, that will change…
Have you check what happens when you rotate device ?
Anyway, in your present design, I would replace all
btn = UIButton(frame: CGRect(x: 8, y: termsTextView.contentSize.height*10-262, width: 48, height: 48))
path = UIBezierPath(rect: CGRect(x: 8, y: termsTextView.contentSize.height*10-262, width: btn!.frame.width, height: btn!.frame.height))by a generic
btn = UIButton(frame: CGRect(x: 8, y: termsTextView.contentSize.height*nbLines - 262, width: 48, height: 48))
path = UIBezierPath(rect: CGRect(x: 8, y: termsTextView.contentSize.height*nbLines - 262, width: btn!.frame.width, height: btn!.frame.height))with nbLines computed for each modelName
var model: (name: String, lines: Int) {
// return the modelName if you still need it and the number of lines ; that will be easier to localize also
}then, replace
switch(UIDevice.current.modelName){
case "iPhone 6 Plus":
btn = UIButton(frame: CGRect(x: 8, y: termsTextView.contentSize.height*10-262, width: 48, height: 48))
path = UIBezierPath(rect: CGRect(x: 8, y: termsTextView.contentSize.height*10-262, width: btn!.frame.width, height: btn!.frame.height))by
let nbLines = UIDevice.current.model.lines
btn = UIButton(frame: CGRect(x: 8, y: termsTextView.contentSize.height*nbLines - 262, width: 48, height: 48))
path = UIBezierPath(rect: CGRect(x: 8, y: termsTextView.contentSize.height*nbLines - 262, width: btn!.frame.width, height: btn!.frame.height))Finally, you could create an UIButton subclass to define checkbox
In addition, nbLines probably just depends on the width of the textView (hence width of screen). Can't you compute it directly, as a function of this width ?