分类: SwiftUI入门

  • 解决SwiftUI中的透明view点击无法生效的问题

    透明的view,无论是整体opacity设置为了0,还是颜色设置为了clear,在swiftUI中,默认的content shape (内容形状)就是0,所以点击事件是无法生效的,而要解决这个问题,我们只需要手动设置content shape即可 代码如下:
    Color.clear
      .frame(width: 300, height: 300)
      .contentShape(Rectangle())     // 这里,当然上面的frame也有必要,如果frame为0,则肯定也无法触发点击
      .onTapGesture { print("tapped") } 
  • SwiftUI中如何自定义Navigation返回按钮

    iOS15+

    // 自定义的返回按钮,按照你的需求自己定制
    struct NavBackButton: View {
        let dismiss: DismissAction
        
        var body: some View {
            Button {
                dismiss()
            } label: {
                Image("...custom back button here")
            }
        }
    }
    
    // 在要使用的view下,加入如下modifier
    .navigationBarBackButtonHidden(true) // Hide default button
    .navigationBarItems(leading: NavBackButton(dismiss: self.dismiss)) // Attach custom button
    在引入了上述逻辑后,就会在child view中显示你的自定义的按钮,但同时,左滑返回的手势会失效,如果你需要保留这个手势,则还需要加入如下的代码:
    // 让使用自定义返回按钮时,左滑返回的动画不失效,同时对child view是scrollview等也需要监听drag gesture的情况做了兼容
    extension UINavigationController: UIGestureRecognizerDelegate {
        override open func viewDidLoad() {
            super.viewDidLoad()
            interactivePopGestureRecognizer?.delegate = self
        }
    
        public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            return viewControllers.count > 1
        }
    
        // To make it works also with ScrollView
        public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            true
        }
    }

    iOS15之前

    struct SampleDetails: View {
        @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    
        var btnBack : some View { Button(action: {
            self.presentationMode.wrappedValue.dismiss()
            }) {
                HStack {
                Image("ic_back") // set image here
                    .aspectRatio(contentMode: .fit)
                    .foregroundColor(.white)
                    Text("Go back")
                }
            }
        }
        
        var body: some View {
                List {
                    Text("sample code")
            }
            .navigationBarBackButtonHidden(true)
            .navigationBarItems(leading: btnBack)
        }
    }
  • SwiftUI 如何让view强制支持横屏或竖屏

    问题:有时候在编写APP时,我们会需要某个页面只支持手机的某个方向(并不是懒得适配,确信)。而在swiftUI中,要实现这个设定又变得更加复杂了,本篇文章就希望提供一个清晰且简单的解法,来支持这一点。

    首先,我们需要swiftUI支持AppDelegate.

    在非swiftUI中,这个文件是在创建项目的时候就会被系统一同创建的,但swiftUI并没有为我们创建这个文件,但这并不意味着swiftUI就无法连接到AppDelegate并作出响应

    而其实建立连接也十分简单,首先,创建一个class,这里其实创建AppDelegate的任何方法都可以,但为了不重复赘述,我这里直接写好了后面屏幕方向设定会用到的方法
    [code language=”objc”]
    class AppDelegate: NSObject, UIApplicationDelegate {
    static var orientationLock = UIInterfaceOrientationMask.portrait

    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -&gt; UIInterfaceOrientationMask {
    return AppDelegate.orientationLock
    }
    }
    [/code]
    这个设定,是将APP支持的屏幕方向设置了只支持竖屏,当然,如果你的APP也仅仅想做到这一步,那其实在设置里也可以直接做到,不用专门写一个class

    接下来,在某个我们希望只支持横屏的view中,只要写下如下代码,即可切换到横屏
    [code language=”objc”]
    import SwiftUI

    struct DestinationView: View {

    var body: some View {
    Group {

    Text("Hello")

    }.onAppear {
    AppDelegate.orientationLock = UIInterfaceOrientationMask.landscapeLeft
    UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
    UINavigationController.attemptRotationToDeviceOrientation()
    }
    .onDisappear {
    DispatchQueue.main.async {
    AppDelegate.orientationLock = UIInterfaceOrientationMask.portrait
    UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
    UINavigationController.attemptRotationToDeviceOrientation()
    }
    }
    }
    }
    [/code]
    要注意的是,UIDevice中设定Value时要使用rawValue,否则会报错,而在onDisappear方法中,要使用异步来保证当前view消失时,不会因为之前的view的屏幕朝向不同而报错

  • SwiftUI 遇到Simultaneous accesses to XXX, but modification requires exclusive access的解决办法

    本质上这个问题是一个系统bug,swiftUI中,如果你的List下不完全是依靠Foreach加载的core data的数据的话(即,你在list中添加了其他的View,例如自己写了个自定义的Title等),而你又提供了删除或者移动row的方法时,就会出现这种错误。

    这是由于swiftUI Foreach的onDelete的方法没有兼容上面所述的情况造成的

    目前可以使用的的解决办法是这样的:
    viewContext.perform {
                offsets.map { molts[$0] }.forEach(viewContext.delete)
    
                do {
                    try viewContext.save()
                } catch {
                    viewContext.rollback()
                    userMessage = "\(error): \(error.localizedDescription)"
                    displayMessage.toggle()
                }
    }
    在你的操作方法外,再套一层,调用viewContext.perform即可
  • SwiftUI CoreData entity/实体更新导致无法预览的问题

    最近改动了某个entity的类型,取消了它的继承,preview突然就停止工作了,一直提示crash,直接调用以下方法,清除之前的preview设定即可,因为preview里还保存了旧的设定,与新设定冲突,导致preview crash
    [code language=”shell”]
    xcrun simctl –set previews delete all
    [/code]

  • 如何在SwiftUI中给字体加粗

    swiftUI的文本设置,给了非常简单易懂的字体选择器,而如果想给字体加粗,该如何实现呢

    这里就可以这样设置

    [code language=”python”]
    TextField("Name", text: $name)
    .font(Font.headline.weight(.light))
    [/code]

    而可以选择的字体大小的,有如下的从小到大的名称
    [code language=”css”]
    .caption
    .footnote
    .subheadline
    .callout
    .body
    .headline
    .title
    .largeTitle
    [/code]

    而字体粗细的选择,则有如下从细到粗的字段可选
    [code language=”css”]
    .ultralight
    .thin
    .light
    .regular
    .medium
    .semibold
    .bold
    .heavy
    .black
    [/code]